// Copyright (C) 2024 Shpz<shaaapz@gmail.com>. All Rights Reserved.

import { Component, Node, NodeSpace, Quat, Vec3, _decorator, quat, v3, clamp } from 'cc';
import { InputSystem } from './InputSystem';
import { SpringArmComponent } from './SpringArmComponent';

const { ccclass, property, executionOrder } = _decorator;

@ccclass('Pawn')
@executionOrder(-1)
export class Pawn extends Component {
    @property
    protected moveSpeed = 1;

    @property
    protected turnSpeed = 2;

    @property
    protected lookUpSpeed = 2;

    protected target: Node;

    protected lookUpRadian = 0;
    protected moveDirection = v3();
    protected lookAt = v3();

    protected springArmComponent: SpringArmComponent;

    start() {
        this.target = this.node.getChildByName("Head");
        this.springArmComponent = this.getComponent(SpringArmComponent);
    }

    update(deltaTime: number) {
        this.handleRotate(deltaTime);

        this.handleMove(deltaTime);
    }

    public possess() {
        this.springArmComponent.enabled = true;
        this.setupInput();
    }

    public unpossess() {
        this.springArmComponent.enabled = false;
        this.unloadInput();
    }

    protected setupInput() {
        InputSystem.inst.bindAxis("MoveForward", this.moveForward, this);
        InputSystem.inst.bindAxis("MoveRight", this.moveRight, this);
        InputSystem.inst.bindAxis("Turn", this.turn, this);
        InputSystem.inst.bindAxis("LookUp", this.lookUp, this);
    }

    protected unloadInput() {
        InputSystem.inst.removeAxis("MoveForward");
        InputSystem.inst.removeAxis("MoveRight");
        InputSystem.inst.removeAxis("Turn");
        InputSystem.inst.removeAxis("LookUp");
    }

    protected handleMove(dt: number) {
        let direction = v3();
        if (this.moveDirection.x > 0) {
            direction.add(Vec3.clone(this.node.right));
        }
        else if (this.moveDirection.x < 0) {
            direction.add(Vec3.negate(v3(), this.node.right));
        }

        if (this.moveDirection.z > 0) {
            direction.add(Vec3.clone(this.node.forward));
        }
        else if (this.moveDirection.z < 0) {
            direction.add(Vec3.negate(v3(), this.node.forward));
        }

        let velocity = direction.normalize().multiplyScalar(this.moveSpeed * dt);
        this.node.translate(velocity, NodeSpace.WORLD);

        this.moveDirection.set();
    }

    protected handleRotate(dt: number) {
        let turnQuat = Quat.fromAxisAngle(quat(), Vec3.UP, this.lookAt.y * this.turnSpeed * dt);
        this.node.rotate(turnQuat);

        let value = this.lookUpRadian;
        let deltaValue = this.lookAt.x * this.lookUpSpeed * dt;
        this.lookUpRadian = clamp(value + deltaValue, -1.57, 1.57);
        deltaValue = this.lookUpRadian - value;

        let lookUpQuat = Quat.fromAxisAngle(quat(), Vec3.RIGHT, deltaValue);
        this.target.rotate(lookUpQuat);

        this.lookAt.set();
    }

    protected moveForward(value: number) {
        this.moveDirection.z = value;
    }

    protected moveRight(value: number) {
        this.moveDirection.x = value;
    }

    protected turn(value: number) {
        this.lookAt.y = value;
    }

    protected lookUp(value: number) {
        this.lookAt.x = value;
    }
}